// SimpleBeanFactory.java
package mini.spring.core;

import mini.spring.annotations.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.beans.Introspector;
import java.util.function.Supplier;

public class SimpleBeanFactory {
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); // 一级缓存
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); // 二级缓存
    private final Map<String, Supplier<Object>> singletonFactories = new ConcurrentHashMap<>(); // 三级缓存

    public void registerBeanDefinition(String name, BeanDefinition bd) {
        beanDefinitionMap.put(name, bd);
    }

    public Object getBean(String name) {
        // 1️⃣ 查一级缓存
        Object bean = singletonObjects.get(name);
        if (bean != null) return bean;

        // 2️⃣ 查二级缓存
        Object earlyBean = earlySingletonObjects.get(name);
        if (earlyBean != null) return earlyBean;
        // 3️⃣ 查三级缓存
        Supplier<Object> factory = singletonFactories.get(name);
        if (factory != null) {
            Object created = factory.get();
            earlySingletonObjects.put(name, created);
            singletonFactories.remove(name);
            return created;
        }
        // 4️⃣ 创建新 Bean
        BeanDefinition bd = beanDefinitionMap.get(name);
        if (bd == null) throw new RuntimeException("No bean named " + name);
        Object newBean = createBean(name, bd);
        return newBean;
    }

    private Object createBean(String beanName, BeanDefinition bd) {
        Class<?> clazz = bd.getBeanClass();
        try {
            // 🔹 实例化阶段
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            Object instance = constructor.newInstance();

            // ➕ 提前暴露：放入三级缓存
            singletonFactories.put(beanName, () -> instance);

            // 🔹 依赖注入阶段
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    String depName = Introspector.decapitalize(field.getType().getSimpleName());
                    Object dependency = getBean(depName);
                    field.set(instance, dependency);
                }
            }

            // 🔹 初始化阶段
            for (Method method : clazz.getDeclaredMethods()) {
                if (method.isAnnotationPresent(PostConstruct.class)) {
                    method.setAccessible(true);
                    method.invoke(instance);
                }
            }

            // 🔹 放入一级缓存
            singletonObjects.put(beanName, instance);
            earlySingletonObjects.remove(beanName);
            singletonFactories.remove(beanName);

            return instance;
        } catch (Exception e) {
            throw new RuntimeException("Failed to create bean: " + beanName, e);
        }
    }

    public void scanPackage(String basePackage) {
        Set<Class<?>> classSet = ClassScanner.scanPackage(basePackage);
        System.out.println(">>> scanPackage('" + basePackage + "') found " + classSet.size() + " classes");
        for (Class<?> clz : classSet) {
            // 防空和接口/注解类过滤（只处理具体类）
            if (clz == null || clz.isInterface() || java.lang.reflect.Modifier.isAbstract(clz.getModifiers())) {
                continue;
            }
            if (clz.isAnnotationPresent(Component.class)) {
                Component comp = clz.getAnnotation(Component.class);
                String name = comp.value();
                if (name == null || name.isEmpty()) {
                    name = Introspector.decapitalize(clz.getSimpleName());
                }
                BeanDefinition bd = new BeanDefinition(clz, name);
                if (clz.isAnnotationPresent(Scope.class)) {
                    bd.setScope(clz.getAnnotation(Scope.class).value());
                }
                registerBeanDefinition(name, bd);
                System.out.println("Registered bean -> name: '" + name + "', class: " + clz.getName());
            }
        }
        System.out.println(">>> current beanDefinitionMap keys: " + beanDefinitionMap.keySet());
    }
}
